#pragma once

#include "../../base/bits.h"
#include "../../base/list.h"
#include "../../base/obj_pointer.h"
#include "../../vmath/mat4.h"

#include <vector>
#include <functional>

OPEN_JLIB_NS


struct TouchEvent;
struct MouseEvent;
struct KeyEvent;
class Action;
class Director;
class alignas(16) Node : public Obj {

public:
  union Order {
    struct {
      s32 major;
      u32 minor;
    };
    s64 order;
  };

  enum Flag : u64 {
    DrawQueueDirty = 1,
    StepQueueDirty = DrawQueueDirty << 1,
    LocalTransformDirty = StepQueueDirty << 1,
    WorldTransformDirty = LocalTransformDirty << 1,
    LocalInverseTransformDirty = WorldTransformDirty << 1,
    WorldInverseTransformDirty = LocalInverseTransformDirty << 1,
    AllTransformDirtyMask = LocalTransformDirty | WorldTransformDirty | LocalInverseTransformDirty | WorldInverseTransformDirty,
    ShouldRemove = WorldInverseTransformDirty << 1,
    CascadeColor = ShouldRemove << 1,
    Visible = CascadeColor << 1,
    Focusable = Visible << 1,
    Volatile = Focusable << 1,
    Touchable = Volatile << 1,
    MouseEnabled = Touchable << 1,
    KeyEnabled = MouseEnabled << 1,
    NodeFlagMax = KeyEnabled,
  };

  template<List Node::*M>
  struct Queue {
    inline Queue (List *list) noexcept : _list(list) {}
    inline List::iterator<Node, M> begin () noexcept { return _list->next(); }
    inline List::iterator<Node, M> rbegin () noexcept { return _list->prev(); }
    inline List::iterator<Node, M> end () noexcept { return _list; }
  private:
    List *_list;
  };

public:
  explicit Node (Director *director) noexcept : _director(director) {}
  ~Node () override;

  inline bool init () { return true; }

  virtual void addChild (Node *child, s32 stepOrder = 0L, s32 drawOrder = 0L);
  virtual void removeSelf (bool cleanup);

  void removeChild (Node* child, bool cleanup = true);
  void removeAllChildren ();

  void setStepOrder (s32 order) noexcept;
  void setDrawOrder (s32 order) noexcept;

  void reorderStepQueue () noexcept;
  void reorderDrawQueue () noexcept;

  virtual Vec3 getPosition () { return _position; }
  virtual Vec3 getRotation () { return _rotation; }
  virtual Vec3 getScale () { return _scale; }
  virtual Vec4 getColor () { return _color; }

  virtual void setPosition (Vec3 v);
  virtual void setRotation (Vec3 v);
  virtual void setScale (Vec3 v);
  virtual void setColor (Vec4 v) { _color = v; }

  virtual const Mat4& getLocalMatrix ();
  virtual const Mat4& getWorldMatrix ();
  virtual const Mat4& getLocalInverseMatrix ();
  virtual const Mat4& getWorldInverseMatrix ();

  virtual void dispatchStep (f32 dt);
  virtual void dispatchDraw (const Mat4 &pv, const Mat4 &p, const Mat4 &v, const Mat4 &m, Vec4 c);

  virtual void onStep (float dt) {}
  virtual void onDraw (const Mat4 &pv, const Mat4 &p, const Mat4 &v, const Mat4 &m, Vec4 c) {}

  virtual bool onKey (const KeyEvent &e) { return false; }

  virtual bool onMouseMove (const MouseEvent &e) { return false; }
  virtual bool onMousePress (const MouseEvent &e) { return false; }
  virtual bool onMouseRelease (const MouseEvent &e) { return false; }
  virtual void onMouseScroll (const MouseEvent &e) {}
  virtual void onMouseCancel (const MouseEvent &e) {}

  virtual bool onInterceptTouchDown (const TouchEvent &e) { return false; }
  virtual bool onInterceptTouchMove (const TouchEvent &e) { return false; }
  virtual bool onTouchDown (const TouchEvent &e) { return false; }
  virtual void onTouchMove (const TouchEvent &e) {}
  virtual void onTouchUp (const TouchEvent &e) {}
  virtual void onTouchCancel (const TouchEvent &e) {}

  void runAction (Action *action);
  void stopAction (Action *action);
  void stopAllActions ();

  inline Director* director () noexcept { return _director; }
  inline Node* parent () noexcept { return _parent; }

  inline s32 stepOrder () const noexcept { return _stepOrder.major; }
  inline s32 drawOrder () const noexcept { return _drawOrder.major; }

  inline s64 stepFullOrder () const noexcept { return _stepOrder.order; }
  inline s64 drawFullOrder () const noexcept { return _drawOrder.order; }

  inline Queue<&Node::_stepSiblings> stepQueue () noexcept { reorderStepQueue(); return &_stepChildren; }
  inline Queue<&Node::_drawSiblings> drawQueue () noexcept { reorderDrawQueue(); return &_drawChildren; }

  inline bool willRemove () const noexcept { return bool(_builtinFlags & ShouldRemove); }
  inline bool isCascadeColorEnabled () const noexcept { return bool(_builtinFlags & CascadeColor); }
  inline bool isVisible () const noexcept { return bool(_builtinFlags & Visible); }
  inline bool isFocusable () const noexcept { return bool(_builtinFlags & Focusable); }
  inline bool isVolatile () const noexcept { return bool(_builtinFlags & Volatile); }
  inline bool isTouchable () const noexcept { return bool(_builtinFlags & (Visible | Touchable | ShouldRemove)); }
  inline bool isMouseEnabled () const noexcept { return bool(_builtinFlags & (MouseEnabled | ShouldRemove)); }
  inline bool isKeyEnabled () const noexcept { return bool(_builtinFlags & (KeyEnabled | ShouldRemove)); }

  inline void removeDelay () noexcept {
    _builtinFlags |= ShouldRemove;
  }

  inline void setCascadeColor (bool enable) noexcept {
    if (enable)
      _builtinFlags |= CascadeColor;
    else
      _builtinFlags &= ~CascadeColor;
  }

  inline void setVisible (bool enable) noexcept {
    if (enable)
      _builtinFlags |= Visible;
    else
      _builtinFlags &= ~Visible;
  }

  inline void setFocusable (bool enable) noexcept {
    if (enable)
      _builtinFlags |= Focusable;
    else
      _builtinFlags &= ~Focusable;
  }

  inline void setVolatile (bool enable) noexcept {
    if (enable)
      _builtinFlags |= Volatile;
    else
      _builtinFlags &= ~Volatile;
  }

  inline void setTouchable (bool enable) noexcept {
    if (enable)
      _builtinFlags |= Touchable;
    else
      _builtinFlags &= ~Touchable;
  }

  inline void setMouseEnable (bool enable) noexcept {
    if (enable)
      _builtinFlags |= MouseEnabled;
    else
      _builtinFlags &= ~MouseEnabled;
  }

  inline void setKeyEnable (bool enable) noexcept {
    if (enable)
      _builtinFlags |= KeyEnabled;
    else
      _builtinFlags &= ~KeyEnabled;
  }

protected:
  Director *_director = nullptr;
  Node *_parent = nullptr;

  List _stepChildren = nullptr;
  List _stepSiblings;
  List _drawChildren = nullptr;
  List _drawSiblings;

  Mat4 _localTransform;
  Mat4 _localInverseTransform;
  Mat4 _worldTransform;
  Mat4 _worldInverseTransform;

  Vec3 _position = Vec3::zero();
  Vec3 _rotation = Vec3::zero();
  Vec3 _scale = Vec3::one();
  Vec4 _color = Vec4::one();

  Order _drawOrder;
  Order _stepOrder;
  u64 _builtinFlags = AllTransformDirtyMask | CascadeColor | Visible;

  static u32 __orderSeed;

private:
  static s64 compareOrder (List *lhs, List *rhs) noexcept;
};


CLOSE_JLIB_NS
